package swimv2gm.bl.session;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
import javax.persistence.Query;

import org.jboss.ejb3.annotation.RemoteBinding;

import swimv2gm.bl.domain.Ability;
import swimv2gm.bl.domain.Account;
import swimv2gm.bl.domain.Admin;
import swimv2gm.bl.domain.User;
import swimv2gm.bl.util.UserData;
import swimv2gm.bl.util.Utilities;

@Stateless
@RemoteBinding(jndiBinding = "UserManager")
public class UserManagerBean implements UserManager {

	@PersistenceContext(unitName = "swimv2")
	private EntityManager manager;

	/**
	 * {@inheritDoc}
	 */
	@Override
	public long createUser(String email,String nome,String cognome,String password) throws ExistingDataException {
		User u = new User();

		if (!Utilities.validate(email,Utilities.EMAIL) || !Utilities.validate(nome,Utilities.REQUIRED)
				|| !Utilities.validate(cognome,Utilities.REQUIRED) || !Utilities.validate(password,Utilities.PASSWORD)) {
			return -1;
		}
		// don't repeat emails
		if (getUserId(email) > 0) {
			throw new ExistingDataException();
		}

		u.setEmail(email);
		u.setNome(nome);
		u.setCognome(cognome);
		u.setPassword(password); // TODO hash

		try {
			manager.persist(u);
		} catch (PersistenceException e) {
			return -1;
		}
		return u.getId();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public long createAdmin(String email,String password) throws ExistingDataException {
		Admin a = new Admin();

		if (!Utilities.validate(email,Utilities.EMAIL) || !Utilities.validate(password,Utilities.PASSWORD)) {
			return -1;
		}
		// don't repeat emails
		if (getUserId(email) > 0) {
			throw new ExistingDataException();
		}

		a.setEmail(email);
		a.setPassword(password);
		try {
			manager.persist(a);
		} catch (PersistenceException e) {
			return -1;
		}
		return a.getId();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public long login(String email,String password) {
		long uid = getUserId(email);
		if (uid < 0) {
			return -1;
		}
		Account a = manager.find(Account.class,uid);
		if (a == null) {
			return -1;
		}
		if (a.getPassword().equals(password)) { // HASH
			return a.getId();
		}
		else {
			return -1;
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean isAdmin(long uid) {
		return manager.find(Admin.class,uid) != null;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public long getUserId(String email) {
		Query q = manager.createQuery("select new java.lang.Long(u.uid) from account u where u.email=:email");
		q.setParameter("email",email);
		List<Long> o = q.getResultList();
		if (o == null || o.size() == 0 || o.size() > 1) {
			return -1;
		}
		else {
			return o.get(0);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Map<String,String> getUserData(long userId) {
		User u = manager.find(User.class,userId);
		if (u == null)
			return null;

		Map<String,String> userData = new HashMap<String,String>();

		userData.put("uid",Long.toString(u.getId()));
		userData.put("email",u.getEmail());
		userData.put("nome",u.getNome());
		userData.put("cognome",u.getCognome());
		userData.put("bio",u.getBio());
		userData.put("indirizzo",u.getIndirizzo());
		userData.put("telefono",u.getTelefono());

		return userData;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean addAbilities(long uid,long[] cid) {
		User u = manager.find(User.class,uid);
		if (u == null)
			return false;

		for (long l:cid) {
			Ability c = manager.find(Ability.class,l);
			if (c == null)
				return false;
			u.addCompetenza(c);
			c.addUtente(u);
		}
		manager.flush();

		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Map<Long,String> getAbilities(long uid) {
		User u = manager.find(User.class,uid);
		if (u == null) {
			return null;
		}
		Set<Ability> alist = u.getCompetenze();

		Map<Long,String> competenze = new HashMap<Long,String>();
		for (Ability a:alist) {
			competenze.put(a.getId(),a.getNome());
		}

		return competenze;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean editProfile(long uid,String email,String nome,String cognome,String telefono,String indirizzo,
			String bio,String password,String newpassword) throws ExistingDataException {
		User u = manager.find(User.class,uid);
		if (u == null)
			return false;

		// stop immediately if the password is wrong
		if (!u.getPassword().equals(password)) {
			return false;
		}

		if (!Utilities.validate(nome,Utilities.REQUIRED) || !Utilities.validate(cognome,Utilities.REQUIRED)
				|| !Utilities.validate(email,Utilities.EMAIL)) {
			return false;
		}

		long eid = getUserId(email);
		if ((eid > 0 && eid != u.getId())) {
			throw new ExistingDataException();
		}

		if (newpassword != null && newpassword.length() > 0 && !Utilities.validate(newpassword,Utilities.PASSWORD)) {
			return false;
		}

		if (telefono != null && telefono.length() > 0 && !Utilities.validate(telefono,Utilities.PHONE)) {
			return false;
		}

		if (!u.getEmail().equalsIgnoreCase(email)) {
			u.setEmail(email);
		}

		if (!u.getNome().equals(nome)) {
			u.setNome(nome);
		}

		if (!u.getCognome().equals(cognome)) {
			u.setCognome(cognome);
		}

		// can be empty (don't change), but must be valid
		if (!u.getPassword().equals(newpassword) && newpassword != null && newpassword.length() != 0) {
			u.setPassword(newpassword);
		}

		if (telefono != null) {
			if (telefono.length() == 0) {
				u.setTelefono(null);
			}
			else if (!telefono.equals(u.getTelefono())) {
				u.setTelefono(telefono);
			}
		}
		else {
			u.setTelefono(null);
		}

		// can be empty
		if (bio != null) {
			if (bio.length() == 0) {
				u.setBio(null);
			}
			else if (!bio.equals(u.getBio())) {
				u.setBio(bio);
			}
		}
		else {
			u.setBio(null);
		}

		if (indirizzo != null) {
			if (indirizzo.length() == 0) {
				u.setIndirizzo(null);
			}
			else if (!indirizzo.equals(u.getIndirizzo())) {
				u.setIndirizzo(indirizzo);
			}
		}
		else {
			u.setIndirizzo(null);
		}

		manager.flush();

		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public List<UserData> search(String nome,String cognome,String indirizzo,String bio,boolean exclusive) {

		String query = "select new swimv2gm.bl.util.UserData(u.uid,u.nome,u.cognome) from utenti u where";

		String operator = exclusive ? "and" : "or";

		// conditions to add
		List<String> conditions = new ArrayList<String>();

		if (nome != null && !nome.equals(""))
			conditions.add("nome=:nome");
		if (cognome != null && !cognome.equals(""))
			conditions.add("cognome=:cognome");
		if (indirizzo != null && !indirizzo.equals(""))
			conditions.add("indirizzo like :indirizzo");
		if (bio != null && !bio.equals(""))
			conditions.add("bio like :bio");

		// no parameters were included, no results
		if (conditions.size() == 0) {
			return null;
		}

		// create query string
		query = query.concat(" u." + conditions.get(0));
		for (int i = 1;i < conditions.size();i++) {
			query = query.concat(" " + operator + " " + conditions.get(i));
		}

		Query q = manager.createQuery(query);

		// query parameters
		if (nome != null && !nome.equals(""))
			q.setParameter("nome",nome);
		if (cognome != null && !cognome.equals(""))
			q.setParameter("cognome",cognome);
		if (indirizzo != null && !indirizzo.equals(""))
			q.setParameter("indirizzo","%" + indirizzo + "%");
		if (bio != null && !bio.equals(""))
			q.setParameter("bio","%" + bio + "%");

		return q.getResultList();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public User getUser(long uid) {
		return manager.find(User.class,uid);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Account getAccount(long id) {
		return manager.find(Account.class,id);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Admin getAdmin(long aid) {
		return manager.find(Admin.class,aid);
	}
}
